package AliYunOssClient

import (
	"bytes"
	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	"io"
	"os"
	"strconv"
)

type aliYunOssClient struct {
	endPoint        string
	accesskeyId     string
	accessKeySecret string

	client *oss.Client
}

func NewAliYunOssClient(endPoint, accesskeyId, accessKeySecret string) AliYunClientInterface {
	return &aliYunOssClient{
		endPoint:        endPoint,
		accesskeyId:     accesskeyId,
		accessKeySecret: accessKeySecret,
	}
}

func (a *aliYunOssClient) getOssClient() error {
	if a.client == nil {
		client, err := oss.New(a.endPoint, a.accesskeyId, a.accessKeySecret)
		if err != nil {
			return err
		}
		a.client = client
	}

	return nil
}

func (a *aliYunOssClient) Put(bucketName, fileName, localFilePath string) error {
	if err := a.getOssClient(); err != nil {
		return err
	}
	bucket, err := a.client.Bucket(bucketName)
	if err != nil {
		return err
	}
	err = bucket.PutObjectFromFile(bucketName, localFilePath)
	if err != nil {
		return err
	}
	return nil
}

func (a *aliYunOssClient) PutStream(bucketName string, fileName string, reader io.Reader) error {
	if err := a.getOssClient(); err != nil {
		return err
	}
	bucket, err := a.client.Bucket(bucketName)
	if err != nil {
		return err
	}

	err = bucket.PutObject(fileName, reader)
	if err != nil {
		return err
	}
	return nil
}

/**
分片上传
*/
func (a *aliYunOssClient) PutStreamChunk(bucketName string, filePath string, fileName string, chunkNum int) error {
	if err := a.getOssClient(); err != nil {
		return err
	}

	bucket, err := a.client.Bucket(bucketName)
	if err != nil {
		return err
	}
	if chunkNum <= 1 {
		chunkNum = 1
	}
	var hashBuffer bytes.Buffer
	hashBuffer.WriteString(bucketName)
	hashBuffer.WriteString(fileName)
	hashBuffer.WriteString(filePath)
	hashBuffer.WriteString(strconv.Itoa(chunkNum))

	uploadID := Hash256(hashBuffer.String())

	// 将本地文件分片，且分片数量指定为3。
	chunks, err := oss.SplitFileByPartNum(filePath, chunkNum)
	if err != nil {
		return err
	}
	fd, err := os.Open(filePath)
	if err != nil {
		return err
	}
	defer fd.Close()

	// 设置存储类型为标准存储。
	storageType := oss.ObjectStorageClass(oss.StorageStandard)
	// 步骤1：初始化一个分片上传事件，并指定存储类型为标准存储。
	imur, err := bucket.InitiateMultipartUpload(fileName, storageType)
	if err != nil {
		return err
	}
	uploadID = imur.UploadID

	// 上传分片。
	var parts []oss.UploadPart
	for _, chunk := range chunks {
		_, _ = fd.Seek(chunk.Offset, os.SEEK_SET)
		// 调用UploadPart方法上传每个分片。
		part, err := bucket.UploadPart(imur, fd, chunk.Size, chunk.Number)
		if err != nil {
			return err
		}
		//fmt.Println("UploadPartNumber: ", part.PartNumber, ", ETag: ", part.ETag)
		parts = append(parts, part)
	}
	// 根据InitiateMultipartUploadResult列举已上传的分片。
	_, err = bucket.ListUploadedParts(imur)
	if err != nil {
		return err
	}
	// 打印已上传的分片。
	//fmt.Println("\nParts:", lsRes.UploadedParts)
	//for _, upload := range lsRes.UploadedParts {
	//	fmt.Println("List PartNumber:  ", upload.PartNumber, ", ETag: ", upload.ETag, ", LastModified: ", upload.LastModified)
	//}
	// 根据objectName和uploadID生成InitiateMultipartUploadResult，然后列举所有已上传的分片。
	var imur_with_uploadid oss.InitiateMultipartUploadResult
	imur_with_uploadid.Key = fileName
	imur_with_uploadid.UploadID = uploadID
	// 列举已上传的分片。
	_, err = bucket.ListUploadedParts(imur_with_uploadid)
	if err != nil {
		return err
	}
	// 打印已上传的分片。
	//fmt.Println("\nListUploadedParts by UploadID: ", uploadID)
	//for _, upload := range lsRes.UploadedParts {
	//	fmt.Println("List PartNumber:  ", upload.PartNumber, ", ETag: ", upload.ETag, ", LastModified: ", upload.LastModified)
	//}
	// 完成分片上传。
	_, err = bucket.CompleteMultipartUpload(imur, parts)
	if err != nil {
		return err
	}
	//fmt.Println("cmur:", cmur)
	return nil
}
